6.3 - SUI Observables


What is an observable?

From my experience it's a special variable type we can use to bind the value of an UI element to it, like having a slider which value is always the same as said variable, or a checkbox which value is always connected to the boolean of the observable. We can also see it like a container for a variable type.

An observable ALWAYS NEED a default value, even if it's just a number or boolean. To declare an observable we do it like so, giving it the type and a default value:


static Observable<float> floatObservable = new(1f); // an observable of type float with default value of 1f
static Observable<bool> boolObservable = new(false); // an observable of type boolean with default value of false
static Observable<string> stringObservable = new(""); // an observable of type string with default value of ""
            

How to use them?

To bind the observable to an UI element we use the .Bind method of the UI element. The observable type must be compatible with the UI element so:

  • Slider: Observable<float>
  • Toggle: Observable<bool>
  • Label: Observable<string>
  • Textbox: Observable<string>

Here is an example with a slider:


static Observable floatObservable = new(1f); // observable of type float

  public static void Create()
  {
      var panel = RegisterNewPanel("panel id", true).Anchor(AnchorType.MiddleCenter).Background(Color.blue, EBackground.RoundedStandard).Size(1280, 720);
      var container = SContainer.Background(Color.green).Anchor(AnchorType.Fill)
                          - SSlider.Text("Slider").Range(0, 100).Value(1).Bind(floatObservable); // binding the observable to the slider

      panel.Add(container);
      ChangeObservableValue();
  }

  private static void ChangeObservableValue()
  {
      floatObservable.Set(50); // changing the observable value, which will be reflected on the slider
  }
            

Note that I also needed to set the default value of the slider to 1 using .Value(1f) since for some reasons it doesn't use the observable default value.

Looking at a game example, observables can be really helpfull. Let's say we want a slider value to always be connected to the player X position. We can do that using a float observable and updating it's value constantly like so:


static Observable PlayerXPosition = new(1f); // crating a float observable

  public static void Create()
  {
      var panel = RegisterNewPanel("panel id", true).Anchor(AnchorType.MiddleCenter).Background(Color.blue, EBackground.RoundedStandard).Size(1280, 720);
      var container = SContainer.Background(Color.green).Anchor(AnchorType.Fill)
                          - SSlider.Text("Slider").Range(Mathf.NegativeInfinity, Mathf.Infinity).Bind(PlayerXPosition); // binding the slider to the observable

      panel.Add(container);
  }

  private static void Update()
  {
      PlayerXPosition.Set(LocalPlayer.Transform.position.x); // updating the observable value constantly
  }
            

Now whenever we move along the X position the value of the slider will update accordingly

We could also make the slider change the player X position using .Notify like so:


static Observable PlayerXPosition = new(1f);

  public static void Create()
  {
      var panel = RegisterNewPanel("panel id", true).Anchor(AnchorType.MiddleCenter).Background(Color.blue, EBackground.RoundedStandard).Size(1280, 720);
      var container = SContainer.Background(Color.green).Anchor(AnchorType.Fill)
                          - SSlider.Text("Slider").Range(Mathf.NegativeInfinity, Mathf.Infinity).Bind(PlayerXPosition).Notify(SetPlayerXPos); // using .Notify to call a method when the slider value is changed

      panel.Add(container);
  }

  private static void SetPlayerXPos(float pos) // called when the value of the slider is changed
  {
      var playerPos = LocalPlayer.Transform.position;
      LocalPlayer.Transform.position = new Vector3(pos, playerPos.y, playerPos.z);
  }

  private static void Update()
  {
      PlayerXPosition.Set(LocalPlayer.Transform.position.x); // updating the observable value constantly
  }
            

The same goes with other types like bool and string.